/* ** por compatibilidad se omiten tildes **
================================================================================
 TRABAJO PRACTICO 3 - System Programming - ORGANIZACION DE COMPUTADOR II - FCEN
================================================================================
  definicion de funciones del manejador de memoria
*/

#include "mmu.h"
#include "game.h"
#include "screen.h"

// es una variable lograr para definir la primera
// página del área libre que está libre.
// El scope se restringe a este archivo.
static unsigned int MMU_FREE_PT;

// esta función me sirve para traducir una posición en la memoria de 
// el mapa
unsigned int position_2_memory(unsigned int x, unsigned int y) {
       // casos circulares
       if (x >= CAMPO_SIZE_X) {
            x -= CAMPO_SIZE_X;
        }

        if (x < 0) {
           x += CAMPO_SIZE_X;
        }


        if (y >= CAMPO_SIZE_Y) {
            y -= CAMPO_SIZE_Y;
        }

        if (y < 0) {
           y += CAMPO_SIZE_Y;
        }

	return INIT_MAPA + x*PAGE_SIZE + y*CAMPO_SIZE_X*PAGE_SIZE;
}


unsigned int malloc_page() {
   unsigned int page_init = MMU_FREE_PT;
   MMU_FREE_PT += PAGE_SIZE; // actualizo la primera página libre 
   return page_init;
}

// Inicializa (crea) todas las estructuras necesarias para el mecanismo de
// Paginación (pero no lo activa). Interesa hacer 'identity mapping' para
// el rango 0x00000000 a 0x003FFFFF. Se tienen que crear 4 page tables
// a partir de la direccion 0x28000, 0x29000, 0x2A000 y 0x2B000.
// Por otra parte, el page diretory tiene que empezar en la direccion
// 0x27000.
// Esta función NO activa la
// Paginación; sólo crea las estructuras necesarias.
void mmu_inicializar_dir_kernel() {
  int i = 0;

  unsigned int* pPD = (unsigned int*) PD_DIR;  // Directorio de Páginas.
  unsigned int* pTKernel0 = (unsigned int*) PT_KERNEL_0;  // Tabla de Páginas 0.
  unsigned int* pTKernel1 = (unsigned int*) PT_KERNEL_1;  // Tabla de Páginas 1.
  unsigned int* pTKernel2 = (unsigned int*) PT_KERNEL_2;  // Tabla de Páginas 2.
  unsigned int* pTKernel3 = (unsigned int*) PT_KERNEL_3;  // Tabla de Páginas 3.

  *((unsigned int*) &pPD[0]) = (PT_KERNEL_0 ) + 0x00000003;
  *((unsigned int*) &pPD[1]) = (PT_KERNEL_1 ) + 0x00000003;
  *((unsigned int*) &pPD[2]) = (PT_KERNEL_2 ) + 0x00000003;
  *((unsigned int*) &pPD[3]) = (PT_KERNEL_3 ) + 0x00000003;

  //
  // Configuramos el resto de las 1024 entradas del PD en valores nulos.
  for(i = 4; i < 1024; i++) {
    *((unsigned int*) &pPD[i]) = 0;
  }

  // identity mapping para el area del kernel y libre
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel0[i]) = PAGE_SIZE*i + 0x00000003;
  }

  // el resto parsa a estar no presente
  for (i = 0; i < 1024; i++) {
     *((unsigned int*) &pTKernel1[i]) = 0x00000000;
  }

  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel2[i]) = 0x00000000;
  }

  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel3[i]) = 0x00000000;
  }


}



void mmu_mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica) {


  unsigned int  numEntradaPD = virtual >> 22;
  unsigned int  numEntradaPT = (virtual >> 12) & 0x3FF;
  unsigned int* pPD = (unsigned int*) cr3;
  unsigned int* pPT = 0x0;

  
  // verificamos si existe la entrada en el directorio de páginas
  if (pPD[numEntradaPD] == 0x00000000) {

    // Pedimos un page frame usando malloc_page() . El valor
    // 0x3 que sumamos corresponde a los atributos de la entrada:
    // lectura/escritura y presente y usuario (111b = 7).
    pPD[numEntradaPD] = malloc_page() + 0x00000007;
  }

  
  // configuramos la entrada de página
  pPT = (unsigned int*) (pPD[numEntradaPD] & 0xFFFFF000);
  
  
  // La dirección física ya debería estar alineada a 4KB. De cualquier
  // manera, ponemos en cero los 3 nibles menos signicativos y sumamos 0x7
  // para los atributos (lectura/escritura y presente (111b = 7)).
  pPT[numEntradaPT] = (fisica & 0xFFFFF000) + 0x00000007;

  
  // Debemos llamar la función 'tlbflush' cada vez que modifiquemos
  // reliza la caché del procesador para las traducciones de
  // direcciones virtuales a físicas.
  tlbflush();
}

void mmu_unmapear_pagina(unsigned int virtual, unsigned int cr3) {
  unsigned int  numEntradaPD = virtual >> 22;
  unsigned int  numEntradaPT = (virtual >> 12) & 0x3FF;
  unsigned int* pPD = (unsigned int*) cr3;
  unsigned int* pPT = 0x0;

  //
  // Existe la entrada en el Directorio de Páginas?
  if (pPD[numEntradaPD] != 0x00000000) {

    pPT = (unsigned int*) (pPD[numEntradaPD] & 0xFFFFF000);
  
    
    
    //
    // Borramos la entrada.
    pPT[numEntradaPT] = 0x00000000;
    
    //
    // Debemos llamar la función 'tlbflush' cada vez que modifiquemos
    // realmente una estructura de paginación. La función 'tlbFlush'
    // actualiza la caché del procesador para las traducciones de
    // direcciones virtuales a físicas.
    tlbflush();
  }

  
}

void mmu_inicializar() {
   MMU_FREE_PT = MMU_FREE_INIT;
}


void  mapear_area_circundante(unsigned int* pPD, unsigned int nroZombi, unsigned int color) {
 if (color == 0) {
	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x1000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi) + 1, 
							dame_posicion_y_rojo(nroZombi)));

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x2000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi) + 1, 
							dame_posicion_y_rojo(nroZombi)) + 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x3000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi) + 1, 
							dame_posicion_y_rojo(nroZombi)) - 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x4000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi), 
							dame_posicion_y_rojo(nroZombi)) + 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x05000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi), 
							dame_posicion_y_rojo(nroZombi)) - 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE +  0x6000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi) - 1, 
							dame_posicion_y_rojo(nroZombi)));

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE +  0x7000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi) - 1, 
							dame_posicion_y_rojo(nroZombi)) - 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x8000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_rojo(nroZombi) - 1, 
							dame_posicion_y_rojo(nroZombi)) + 1);

  } else {
	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x1000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi) - 1, 
							dame_posicion_y_azul(nroZombi)));

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x2000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi) - 1, 
							dame_posicion_y_azul(nroZombi)) - 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x3000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi) - 1, 
							dame_posicion_y_azul(nroZombi)) + 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x4000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi), 
							dame_posicion_y_azul(nroZombi)) - 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x5000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi), 
							dame_posicion_y_azul(nroZombi)) + 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x6000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi) + 1, 
							dame_posicion_y_azul(nroZombi)));

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x7000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi) + 1, 
							dame_posicion_y_azul(nroZombi)) + 1);

	  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE + 0x8000, (unsigned int) pPD, 
			(unsigned int) position_2_memory(dame_posicion_x_azul(nroZombi) + 1, 
							dame_posicion_y_azul(nroZombi)) - 1);

  }
 
}

/**
* copiar_pegar(unsigned int nroZombi, unsigned int color)
*
* función se llama para desplazar al zombie
* copiar el código a la nueva parte del mapa
* y remapear el entorno
*/
void copiar_pegar(unsigned int nroZombi, unsigned int color) {
  unsigned int* mapa = 0;
  unsigned int pPD = 0;

  int i = 0;

  if (color == 0) {
	  pPD = dame_cr3(nroZombi, 0);
	  mapa = (unsigned int *) position_2_memory(dame_posicion_x_rojo(nroZombi), 
								dame_posicion_y_rojo(nroZombi));
  } else {
	  pPD = dame_cr3(nroZombi, 1);
	  mapa = (unsigned int *) position_2_memory(dame_posicion_x_azul(nroZombi), 
								dame_posicion_y_azul(nroZombi));
  }

  // mapea el mapa para poder escribir en él
  mmu_mapear_pagina( (unsigned int) mapa, (unsigned int) pPD, (unsigned int) mapa);


  // copia el código de la tarea
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &mapa[i]) =  *( &((unsigned int*)(INIT_VIRTUAL_TASK_CODE))[i]);
  }

  // desmapea el mapa
  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE, pPD, (unsigned int) mapa);

  // desmapea la zona del mapa
  mmu_unmapear_pagina( (unsigned int) mapa, (unsigned int) pPD);

  mapear_area_circundante((unsigned int *) pPD, nroZombi, color);


}
unsigned int* ajustar_mmu_dir_zombi(unsigned int nroTarea, unsigned int color, unsigned int tipoZombie) {
  int i = 0;

  unsigned int * mapa = 0;
  unsigned int* pPDKernel = (unsigned int*) PD_DIR;  // Directorio de Páginas del Kernel.
  if (color == 0) {
	  mapa = (unsigned int*) position_2_memory(dame_posicion_x_rojo(nroTarea), 
								dame_posicion_y_rojo(nroTarea));

  } else {
	  mapa = (unsigned int*) position_2_memory(dame_posicion_x_azul(nroTarea), 
								dame_posicion_y_azul(nroTarea));

  }

  mmu_mapear_pagina( (unsigned int) mapa, (unsigned int) pPDKernel, (unsigned int) mapa);

  unsigned int* pPD = (unsigned int*) malloc_page();  // Directorio de Páginas.
  unsigned int* pTTarea0 = (unsigned int*) malloc_page();  // Tabla de Páginas 0.


  *((unsigned int*) &pPD[0]) = ((unsigned int) pTTarea0 ) + 0x00000007;

  for(i = 4; i < 1024; i++) {
    *((unsigned int*) &pPD[i]) = 0;
  }


  // identity mapping para el area del kernel y libre
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTTarea0[i]) = PAGE_SIZE*i + 0x00000007;
  }


  unsigned int base = 0;
  if (color == 0)
  	base = PT_CODIGO_TASK_ROJO;
  else
  	base = PT_CODIGO_TASK_AZUL;
	


  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &mapa[i]) =  *( &((unsigned int*)(base + tipoZombie*0x1000))[i]);
  }


  
  mmu_mapear_pagina(INIT_VIRTUAL_TASK_CODE, (unsigned int) pPD, (unsigned int) mapa);

  mapear_area_circundante(pPD, nroTarea, color);

  return pPD;
}








